package DataLayer.ImageDataContainer.ImageComponentsInfo;

import java.awt.Point;
import java.awt.geom.Line2D;
import java.io.File;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

public class ImageComponentsInfo implements IImageComponentsInfo {

    private Document imgComp;

    private ArrayList<SingleCharImageInfo> charInfo;

    /**
     * constructor;
     * @throws ParserConfigurationException
     */
    public ImageComponentsInfo() throws ParserConfigurationException {
            //Create instance of DocumentBuilderFactory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            //Get the DocumentBuilder
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            //Create blank DOM Document
            imgComp = (Document) docBuilder.newDocument();

            Element root = imgComp.createElement("file");
            imgComp.appendChild(root);

    }

    public Document getImageComponentsInfo() {
        // TODO add implementation and return statement
        return imgComp;
    }

    public void setImageComponentsInfo(int compInfo) {
        // TODO add implementation
    }

    /**
     * expands the existing document with the line segmentation information
     * @param lines line coordinates
     */
    public void addLines(ArrayList<Point> lines){
        int n = lines.size();

        Node root = imgComp.getFirstChild();

        for(int i = 0; i < n; ++i)
        {
            Element child = imgComp.createElement("line");
            child.setAttribute("koord", lines.get(i).x + " " + lines.get(i).y);
            ////System.out.println("line added");
            // writes the first and second coordinate of the line
            root.appendChild(child);
        }
    }

     /**
     * returns the line coordinates based on the dom
     * @return
     */
    public ArrayList<Point> getLines(){
        // gets all the "line" nodes
         NodeList lines = imgComp.getElementsByTagName("line");
         ArrayList linesArray = new ArrayList<Point>();

         int n = lines.getLength();
         for(int i = 0; i<n; ++i){
             NamedNodeMap coord = lines.item(i).getAttributes();
             StringTokenizer st = new StringTokenizer(coord.item(0).getTextContent());             
             Integer x = new Integer(st.nextToken());
             Integer y = new Integer(st.nextToken());
             Point p = new Point(x, y);
             linesArray.add(p);
         }
         return linesArray;
    }

    
     /**
     * adds the word segmentation information of the image line
     * @param p coordinates of the lines
     * @param words coordinates of all the segmentated words
     */
    public void addWords(ArrayList<ArrayList<Point>> words){
        Node root = imgComp.getFirstChild();
        int m = words.size();
        Node line = root.getFirstChild();

        for(int i = 0; i < m; ++i){

            int n = words.get(i).size();
            if(i>0)
                line = line.getNextSibling();

            for(int j = 0; j < n; ++j){
                Element child = imgComp.createElement("word");
                child.setAttribute("koord", words.get(i).get(j).x + " " + words.get(i).get(j).y);
                
                line.appendChild(child);
                ////System.out.println("Word with coordinates "+ words.get(i).get(j).x + " "+ words.get(i).get(j).y + " added to line nr. "+i);
            }
        }
    }


     /**
     * returns the words coordinates based on the dom
     * @return array list with word coordinates
     */
    public ArrayList<Line2D> getWords(){
        // gets all the "word" nodes
         NodeList words = imgComp.getElementsByTagName("word");
         ArrayList wordsArray = new ArrayList<Line2D>();

         int n = words.getLength();
         for(int i = 0; i<n; ++i){
             NamedNodeMap wordCoord = words.item(i).getAttributes();
             StringTokenizer st1 = new StringTokenizer(wordCoord.item(0).getTextContent());
             
             Integer y1 = new Integer(st1.nextToken());
             Integer y2 = new Integer(st1.nextToken());
             ////System.out.println(this.getClass() + " y1: "+y1 + "y2: "+y2);

             // finding the respective line coordinates:
             NamedNodeMap lineCoord = words.item(i).getParentNode().getAttributes();
             StringTokenizer st2 = new StringTokenizer(lineCoord.item(0).getTextContent());
             
             Integer x1 = new Integer(st2.nextToken());
             Integer x2 = new Integer(st2.nextToken());
             ////System.out.println(this.getClass() + " x1: "+x1 + "x2: "+x2);

             Line2D coord = new Line2D.Double(x1, y1, x2, y2);
             wordsArray.add(coord);

         }
         return wordsArray;
    }


    /**
     * adds the character segmentation information of the words
     * @param words the coordinates of the words
     */
     public void addCharacterCoords(ArrayList<ArrayList<Point>> chars) {
         ////System.out.println(this.getClass() + ": Adding characters");
         // gets all the "word" nodes
         NodeList words = imgComp.getElementsByTagName("word");

         // getting the number of words;
         // should be equal to words.size()
         int wordsCount = chars.size();

         for(int i = 0; i<wordsCount; ++i){
                 ////System.out.println(chars.get(i).size());
             // adding characters to the i. word:
             for(int j = 0; j < chars.get(i).size(); ++j){
                Element child = imgComp.createElement("character");
                // the * is for the recognized letter; yet unknown
                child.setAttribute("koord", chars.get(i).get(j).x + " "+ chars.get(i).get(j).y + " *");
                words.item(i).appendChild(child);
                ////System.out.println("Character with coordinates "+chars.get(i).get(j).getX() + " "+ chars.get(i).get(j).getY() + " added to word nr. "+i);
             }
         }
    }
     

    /**
     * inserts the segmentation information of a (single) given word to characters
     * @param word the word to segmentate
     * @param wordID ID of the segmentated word
     * @param charCoords coordunates of the segmentated charecters
     */
    public void insertWordInformationInImageComponents(String word, int wordID, ArrayList<Integer> charCoords) {
         ////System.out.println(this.getClass() + " Adding characters");
         // gets all the "word" nodes
         NodeList words = imgComp.getElementsByTagName("word");
         // finds the word with the given ID
         Node actWord = words.item(wordID);

         // charCount gives the number of the borders between characters plus
         // the beginning and ending coordinates
         int charCount = charCoords.size();

         for(int i = 0; i<charCount-1; ++i){
                Element child = imgComp.createElement("character");
                child.setAttribute("koord", charCoords.get(i) + " "+ charCoords.get(i+1) + " " + word.substring(i, i+1));
                actWord.appendChild(child);
                ////System.out.println("Character "+word.substring(i, i+1)+" with coordinates "+charCoords.get(i) + " "+ charCoords.get(i+1) + " added to word nr. "+wordID );
         }

////        try {
////            saveToFile("E:\\abc.xml");
////        } catch (Exception ex) {
////            System.out.println("no save");
////        }
////        System.out.println("DOM SAVED TO XML");
    }

    /**
     * gets the characters by their coordinates and meaning
     * @return the characters based on the XML
     */
    public ArrayList<SingleCharImageInfo> getAllCharImageInfo(){

        NodeList chars = imgComp.getElementsByTagName("character");

        int charCount = chars.getLength();
        charInfo = new ArrayList<SingleCharImageInfo>();

        for(int i = 0; i<charCount; ++i){
             NamedNodeMap charCoord = chars.item(i).getAttributes();
             StringTokenizer st1 = new StringTokenizer(charCoord.item(0).getTextContent());

             // getting the y coordinates of the characters from inside the words:
             Integer yyy1 = new Integer(st1.nextToken());
             Integer yyy2 = new Integer(st1.nextToken());
             String ch = st1.nextToken();

             // getting the parent word node:
             Node parent = chars.item(i).getParentNode();
             NamedNodeMap wordCoord = parent.getAttributes();

             StringTokenizer st2 = new StringTokenizer(wordCoord.item(0).getTextContent());

             // getting the y coordinates of the words from inside the lines
             Integer yy1 = new Integer(st2.nextToken());
             Integer yy2 = new Integer(st2.nextToken());

             int y1 = yyy1 + yy1;
             int y2 = yyy2 + yy1;

             Node grandParent = parent.getParentNode();
             NamedNodeMap lineCoord = grandParent.getAttributes();

             StringTokenizer st3 = new StringTokenizer(lineCoord.item(0).getTextContent());

             // getting the x coordinates of the lines:
             Integer x1 = new Integer(st3.nextToken());
             Integer x2 = new Integer(st3.nextToken());

             // the new character starts a new word
             if((i!=0) && (chars.item(i-1).getParentNode()!=parent)){
                SingleCharImageInfo space = new SingleCharImageInfo(x1, y1, x2, y2, "_");
                charInfo.add(space);
             }
             
             SingleCharImageInfo newChar = new SingleCharImageInfo(x1, y1, x2, y2, ch);
////             System.out.println("---------------------------" + ch);
             charInfo.add(newChar);
        }

        return charInfo;
    }

    /**
     * saves the dom in an .xml wile with the given name
     * @param fileName name of the file to be saved
     * @throws Exception
     */
    public void saveToFile(String fileName) throws Exception{
            Transformer transformer = TransformerFactory.newInstance(  ).newTransformer(  );
            Source source = new DOMSource( imgComp );
            File file = new File(fileName);
            Result output = new StreamResult(file);
            //Result output = new StreamResult( System.out );
            transformer.transform( source, output );
    }


    /**
     * refreshes the character information in the dom
     */
    public void updateInformation() {
        NodeList chars = imgComp.getElementsByTagName("character");

        int n = charInfo.size();
////        System.out.println("Ekkora hosszu kene legyen: " + charInfo.size() + " " + n);

        // refreshing each "character" node:
        int j = -1;
        for(int i = 0; i<n; ++i){
            ////System.out.println(charInfo.get(i).ch);
            String act = charInfo.get(i).ch;
            if(act.compareTo("_")!=0)
            {
                ++j;
                String chInfo =  chars.item(j).getAttributes().item(0).getTextContent();
                String temp = new String();
                temp = chInfo.substring(0, chInfo.length()-1);
                temp = temp + charInfo.get(i).ch;
                chars.item(j).getAttributes().item(0).setTextContent(temp);
            }
        }

////        try {
////            saveToFile("E:\\abc.xml");
////        } catch (Exception ex) {
////            System.out.println("no save");
////        }
////        System.out.println("DOM SAVED TO XML");
    }

    /**
     * builds up a text based on the information provided by the dom
     * @return the text builded up based on the dom
     */
    public String getRetreivedText() {
        NodeList chars = imgComp.getElementsByTagName("character");

        int charCount = chars.getLength();
        String text = new String();

        for(int i = 0; i<charCount; ++i){
             NamedNodeMap charCoord = chars.item(i).getAttributes();
             StringTokenizer st1 = new StringTokenizer(charCoord.item(0).getTextContent());

             // getting character:
             st1.nextToken();
             st1.nextToken();
             String ch = st1.nextToken();


             // the new character starts a new word
             if((i!=0) && (chars.item(i-1).getParentNode()!=chars.item(i).getParentNode())){
                if((i!=0) && (chars.item(i-1).getParentNode().getParentNode()!=chars.item(i).getParentNode().getParentNode()))
                    text = text + System.getProperty("line.separator");  // the character lies on the start of a new line
                else
                    text = text + " ";    // the character lies on the start of a new word
             }

             text = text + ch;

        }

        return text;
    }


}
